﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using MathNet.Numerics.LinearAlgebra.Double;

namespace RosenblattPerceptronCS
{
    public class Point
    {
        DenseVector _x;
        int _alpha;
        double _label;

        public DenseVector X_Vector { get { return _x; } }
        public int Alpha { get { return _alpha; } }
        public double Label { get { return _label; } }

        public Point(DenseVector x_value, int alpha_value, string label)
        {
            _x = x_value;
            _alpha = alpha_value;
            _label = Program.labels[label];// == "Iris-setosa" ? 1.0 : -1.0;
        }
        public Point(string s)
        {
            var elems = s.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            var d_components = new double[elems.Length];
            for (int i = 0; i < d_components.Length - 1; i++)
            {
                d_components[i] = double.Parse(elems[i]);
            }
            d_components[elems.Length - 1] = 1.0;
            _x = new DenseVector(d_components);
            _alpha = 0;
            _label = Program.labels[elems[elems.Length - 1]];// == "Iris-setosa" ? 1.0 : -1.0;
        }

        public double GetDistance(DenseVector w)
        {
            return w * _x;
        }

        public double ComputeMargin(DenseVector w)
        {
            return GetDistance(w) * _label;
        }

        public Tuple<bool,DenseVector> ComputePoint(DenseVector w)
        {
            if (this.ComputeMargin(w) > 0.0)
            {
                return Tuple.Create(true, w);
            }
            else
            {
                this._alpha++;
                var _w = w + (Program.RATE * _label * _x);
                return Tuple.Create(false, _w);
            }            
        }
    }

    class Program
    {
        public static double RATE = 0.1;
        public static int MAX_POINT_MISS = 10;
        public static Dictionary<string, int> labels = new Dictionary<string, int>();

        static void Main(string[] args)
        {
            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("en");
            System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture("en");

            labels.Add("Iris-setosa", -1);
            labels.Add("Iris-versicolor", -1);
            labels.Add("Iris-virginica", 1);

            var text = File.ReadAllLines("iris.data");

            var plant = labels.Where(s => s.Value == 1).First().Key;
            
            var out_f = File.CreateText(plant + ".txt");

            out_f.WriteLine("Classifining " + plant + " with rate " + RATE + " and upper boundary " + MAX_POINT_MISS);


            var points =
                (
                    from line in text
                    where line != ""
                    select new Point(line)
                ).ToArray();

            bool all_correct = false;
            int errors = 0, dropped = 0;
            DenseVector w = new DenseVector(new double[] { 0.0, 0.0, 0.0, 0.0, 0.0 });

            System.Diagnostics.Stopwatch chrono = new System.Diagnostics.Stopwatch();
            chrono.Start();
            while (!all_correct)
            {
                out_f.WriteLine("Temporary W : " + w);
                errors = 0;
                all_correct = true;
                foreach (var p in points)
                {
#if !CLASSIC
                    if (p.Alpha >= MAX_POINT_MISS)
                    {
                        dropped++;
                        continue;
                    }
#endif
                    var res = p.ComputePoint(w);
                    w = res.Item2;
                    if (!res.Item1)
                    {
                        errors++;
                        all_correct = false;
                    }
                }
                out_f.WriteLine(" with " + errors + " missclassifications and " + dropped + " dropped points.");
            }
            chrono.Stop();
            //Console.WriteLine(chrono.Elapsed);

            out_f.WriteLine("W : " + w);
            out_f.WriteLine("Time : " + chrono.Elapsed);

            out_f.Close();

            //Console.ReadLine();
        }
    }
}
